package com.seestech.sell.common.config.shiro;


import com.seestech.sell.domain.model.User;
import com.seestech.sell.service.IMenuService;
import com.seestech.sell.service.IUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;


/**
 * ClassName:MyRealm <br/>
 * Function: 指定Shiro验证用户登录 <br/>
 * Date:     2016年7月22日 上午11:15:55 <br/>
 * @author   idiot
 * @version
 * @see
 */
public class MyRealm extends AuthorizingRealm {

	private Logger logger = LoggerFactory.getLogger(AuthorizingRealm.class);

	@Resource
	private IUserService userService;

	@Resource
	private IMenuService menuService;


/**
	 * 授权
	 * 当前登录的Subject授予角色和权限
	 * @description  经测试:本例中该方法的调用时机为需授权资源被访问时
     *  经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
     *  个人感觉若使用了Spring3.1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache
     *  比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache
	 */

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		logger.info("开始授权...");
		//获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()
        String currentUsername = (String)super.getAvailablePrincipal(principals);
        //获取用户信息
        User user = userService.getUserByName(currentUsername);
        if(user == null)
        	throw new AuthorizationException();// 抛出异常 shiro异常处理器接收
        SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
        //获取用户的权限  根据上面获取的user对象查找该用户所拥有的权限  [从数据库中查询]
        logger.info("获取用户对应权限...");
        List<String> permissions = menuService.getPermissionsByUserId(user.getUserId());
        if(permissions != null && !permissions.isEmpty())
        	simpleAuthorInfo.addStringPermissions(permissions);//授权
        else
        	simpleAuthorInfo.addStringPermissions(new ArrayList<String>());
        logger.info("授权完成...");
        return simpleAuthorInfo;
	}


/**
	 * 认证
     * 验证当前登录的Subject
     * @description  经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时
     */

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		logger.info("开始认证...");
		//获取基于用户名和密码的令牌
        //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
        //两个token的引用都是一样的
        UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
        //从token里面拿到username
        String userName = token.getUsername();
        User user = userService.getUserByName(userName);//获取用户
        if(user == null)
        	throw new AuthenticationException();
        SimpleAuthenticationInfo info = null;
        info = new SimpleAuthenticationInfo(userName, user.getUserPassword(), this.getName());
        clearCache(info.getPrincipals());//清除缓存
        logger.info("认证成功...");
		return info;
	}

}

